创建时间: | 2018/7/27 15:19 |
来源: | https://www.cnblogs.com/waterystone/p/5546312.html |
一、懒汉式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | public class Singleton { private volatile static Singleton instance; //声明成 volatile private Singleton (){} public static Singleton getSingleton() { if (instance == null ) { synchronized (Singleton. class ) { if (instance == null ) { instance = new Singleton(); } } } return instance; } } |
使用DCL进行锁的双重检查。这里的volatile是亮点:设置内存屏障,防止instance未初始化完就被泄漏出来。(volatile语义不清楚的可能参考:Java并发编程:volatile关键字解析)
1 2 3 4 5 6 7 8 9 10 | public class Singleton{ //类加载时就初始化 private static final Singleton instance = new Singleton(); private Singleton(){} public static Singleton getInstance(){ return instance; } } |
这种写法简单明了,也线程安全。缺点是它不是一种懒加载模式(lazy initialization),单例会在加载类后一开始就被初始化,即使客户端没有调用 getInstance()方法。
1 2 3 4 5 6 7 8 9 | public class Singleton { private static class SingletonHolder { private static final Singleton INSTANCE = new Singleton(); } private Singleton (){} public static final Singleton getInstance() { return SingletonHolder.INSTANCE; } } |
这种写法仍然使用JVM本身机制保证了线程安全问题;由于 SingletonHolder 是私有的,除了 getInstance() 之外没有办法访问它,因此它是懒汉式的;同时读取实例的时候不会进行同步,没有性能缺陷;也不依赖 JDK 版本。
一般情况下直接使用饿汉式就好了,如果明确要求要懒加载(lazy initialization)会倾向于使用静态内部类。
但以上几种方式都无法保证反射出来还是单例。可以通过如下代码验证:
1 2 3 4 5 6 7 8 9 | //获得构造器 Constructor con = Singleton. class .getDeclaredConstructor(); //设置为可访问 con.setAccessible( true ); //构造两个不同的对象 Singleton singleton1 = (Singleton)con.newInstance(); Singleton singleton2 = (Singleton)con.newInstance(); //验证是否是不同对象 System.out.println(singleton1.equals(singleton2)); |
网上流传一种方法,可以利用enum!有了enum语法糖,JVM会阻止反射获取枚举类的私有构造方法。
1 2 3 | public enum SingletonEnum { INSTANCE; } |
参考: